אהלן אלכס!
עברתי כרגע על מדריכים בphpguide והגעתי למדריך הבא:
http://phpguide.co.il/שיפור_ביצועי_מסד_עם_MySQLI.htm

עכשיו בפסקה הזאת ״כתיבת מעטפת לנוחות מירבית״, אני רואה שהקונסטרקטור הוא התחברות למסד ושהעמוד עצמו כן יורש את מחלקת mysqli. בספר ציינת בפסקה ״מה מותר ומה אסור לעשות בקונסטרקטור״ את הדברים הבאים:
״אחת השגיאות שמתכנתים רבים עושים – היא להכניס לוגיקה לתוך הקונסטרקטור. להתחבר למסד נתונים, לבדוק הזדהות, לקרוא קבצים, לשלוח בקשות לשרתים אחרים או לבצע כל מיני פעולות אחרות שהתוכנה שלהם צריכה לעשות.

הבעיה היחידה היא שהקונסטרקטור בכלל לא מיועד בשביל זה. המטרה היחידה של הקונסטרקטור היא להעביר למחלקה ערכים שהמחלקה תצטרך בשביל תפקוד המתודות האחרות שלה. אם אין לך נתונים שאתה צריך להעביר למחלקה – אתה לא צריך קונסטרקטור.״

וגם באחת הפסקאות של שאלות תשובות ציינת שזה לא בסדר שמוד ירוש את מחלקת mysqli.

אשמח להסבר למה בכל זאת עשית את זה.. כי זה די בילבל אותי.
תודה מראש!

11 תשובות

avatar ענה Splash ב 07 ליוני 2014 #

במקרה של התחברות למסד זה לא כזה משנה משום שגם ככה אתה לא יכול לבצע שום פעולות על הדטאבייס ללא התחברות כך שאם תתחבר בבנאי לא קרה אסון ויש אנשים שאפילו מעדיפים לכתוב את זה ככה בכוונה על מנת למנוע ניסיון להריץ שאילתה ללא התחברות

לגבי ההורשה ,זה מאוד מיותר,כנראה היה להדגמה יותר מאשר משהו שמומלץ להשתמש בו.
בנוסף אתה צריך לשים לב לתאריך שבו הנושא נכתב,לפני כמעט 4 שנים.

avatar ענה intval ב 07 ליוני 2014 #

צריך לתקן את המדריך :)
המטרה של המדריך הייתה להראות עבודה עם MySQLI ולצורך הפשטות ההתחברות נמצאת בתוך הקונסטרקטור.
מצד שני, דווקא במקרה של מסד נתונים (mysqli, pdo) יצירת מופע של מחלקות האלה גורם ליצירת חיבור למסד ולכן במחלקה היורשת החיבור גם נוצר בקונסטרקטור. אגב, אפשר לציין שבשפות תכנות אחרות מחלקות של חיבור למסד נתונים לא מבצעות את החיבור בפועל למסד בבנאי, אלא רק לאחר קריאה למטודה מסוימת.

הדרך הנכונה יכולה להיות כזו:

<?

class db
{
    private $connection = null;

    public function __construct($host, $user, $pass, $db, $charset = 'utf8')
    {
    // if(!$host) throw ..
   
        $this->host = $host;
    $this->user = $user;
    $this->pass = $pass;
    $this->db = $db;
    $this->charset = $charset;
    }
   
  private function ensureConnection()
  {
    if(!$this->connection)
      $this->connection = new mysqli($this->host, $this->user, $this->pass, $this->db);
       
    $this->connection->set_charset($charset);
  }
 
    public function query($query, $resultmode = MYSQLI_STORE_RESULT )
    {
    $this->ensureConnection();
    retpun $this->connection->query($query, $resultmode);
  }

}


$dbWrapper = new db('localhost','my_user','my_password', 'my_db', 'utf8');
?>

אבל שים לב שבמקרה הזה אין קשר של ירושה בין db לבין mysqli בגלל שdb כאן היא לא מחלקה מסוג חיבור למסד נתונים, היא מחלקה שעושה חיבור למסד ופעולות אחרות.


לגבי עמוד יורש ממסד - class db extends mysqli
המעטפת יורשת (ומרחיבה) את מחלקת mysqli אבל אין כאן שום קשר לשום עמוד.
תשאל את עצמך "האם עמוד זה סוג של חיבור למסד" והתשובה היא לא. לאומת זאת האם "מחלקת החיבור שלי היא סוג של חיבור למסד" והתשובה היא כן.

avatar ענה undefined ב 07 ליוני 2014 #

@intval @Splash
1. אם הבנתי נכון, אז בעצם בקונסטרקטור יש את הערכים שהמתודה ensureConnection צריכה? ובנוגע למה שכתוב בהערה, ניתן בקונסטרקטור לבדוק גם האם המשתנים ריקים ואם כן לזרוק שגיאות?
2. אז בעצם כל מחלקה שתירוש את pdo/mysqli הקונסטרקטור שלה אוטומטית יתחבר למסד? ומה אם אין לה קונסטרוקטור?
3. ושאלה עכשיו שקצת לא קשורה לנושא, ברגע שאני משתמש בprepared statements אין צורך בהברחת תווים באמצעות real_escape_string?

avatar ענה intval ב 07 ליוני 2014 #

1. בדיוק. ניתן לבדוק את הערכים שהועברו ולשמור אותם לשימוש מאוחר יותר
2. רק אם תפעיל את parent::construct.
3. נכון.

avatar ענה Splash ב 07 ליוני 2014 #

לפי דעתי זה מאוד מיותר בכל פעם שרוצים להריץ שאילתה לבדוק אם החיבור קיים,למה לא ליצור את החיבור בבנאי ולזרוק חריג במידה ולא היה ניתן להתחבר ?

אני מסכים שבבנאי לא צריך להיות תנאים שעלולים לפגוע בגמישות של הקוד אך זה לא המקרה משום שעל מנת להריץ שאילתה חובה להתחבר למסד.

avatar ענה undefined ב 07 ליוני 2014 #

@intval , יש משהו בדבריו של splash.. יש מצב להסבר על ההיגיון הזה? תודה רבה מראש

avatar ענה intval ב 07 ליוני 2014 #

זה הגיוני בהחלט ולכן זה מה שקורה עם מחלקות pdo ו mysqli שבבנאי שלהם מבצעות חיבור בפועל.
שניכם צודקים וכנראה צריך לתקן את זה גם בספר, שמחלקות שמייצגות משאבים - בבנאי שלהם אמורות ליצור (או להתחבר) למשאב.
כנ"ל כנראה עבור מחלקה שמייצגת קובץ או חיבור רשת. ששם יצירת מופע של מחלקה מבחינת המתכנת זו הדרך ההגיונית לומר בקוד שהוא מתכוון לעשות משהו עם המשאב

avatar ענה undefined ב 07 ליוני 2014 #

@intval , אז אפשר להתחבר למסד בקונסטרקטור..? לא הבנתי מה צריך לעדכן בספר הדיון הזה בלבל אותי. שלחתי לך מייל לפני כמה ימים על PDO והבאת לי את הAUTOLOADING, תוכל לשלוח לי תיקון של הספר?

avatar ענה intval ב 07 ליוני 2014 #

אין כרגע שום תיקון של הספר,
אבל בספר רשום שלא כדאי לעשות שום פעולות משמעוטיות בבנאי, אילו במקרה של מחלקות שמייצגות משאבים, כמו חיבור למסד, קובץ בדיסק או חיבור רשת - הגיוני שהבנאי ייצור גם החיבור

avatar ענה undefined ב 07 ליוני 2014 #

@intval , אז במחלקת הזדהות (התחברות), בקונסטרקטור פשוט לבדוק אם מבנה המשתמש תקין (לא מכיל סימנים מיוחדים וכו׳), ואת ההתחברות למסד להגדיר כמאפיין שאני אשתמש בו בכל מתודה במחלקה? הכוונה שאני רואה שהגדרת את המאפיין connection, ובמתודה ensureConnection בדקת אם הוא לא קיים, ומכיוון שהוא לא (הגדרת אותו כnull) אז יצרת חיבור ששווה למאפיין הזה. אז עכשיו המתודה ensureConnection אחראית על החיבור למסד, ובכל מתודה אחרת בעצם תיצור איתה חיבור מכיוון שהוא לא קיים בבנאי? הבנתי נכון? דבר שני, זה יעיל בכלל?

2. אשמח להסבר על התוכן של המתודה query.

3. אתה ממליץ לעשות מחלקה כללית של משתמש ובתוכה מתודה להתחברות, מתודה להרשמה וכו׳ או class להרשמה ולהתחברות בנפרד?

--
סליחה על כל השאלות, חשוב לי להבין את זה כמו שצריך ואני מעריך את זה מאוד.
תודה

avatar ענה intval ב 07 ליוני 2014 #

מחלקת ההזדהות שלך לא צריכה שום שם משתמש בבנאי.
היא צריכה מתודה שמקבלת שם משתמש וזהו.

אם מחלקת ההזדהות עובדת מול מסד נתונים - היא צריכה לקבל בבנאי מופע של חיבור למסד נתונים כדי שמאוחר יותר (במתודות אחרות) היא תוכל להישתמש בחיבור הזה

class Auth
{
  private $connection;
 
  public function __consturt(IDbConnection $con)
  {
    $this->connection = $con;
  }
 
  public function auth($login, $pass)
  {
    if(!$login || empty($login) || wrongFormat($login))
      return false;
     
    $userInfo = $this->connection->query(.. where username = $login )
   
    return $userInfo && $userInfo->password === $pass;
  }
}